#include <iostream>
#include <sstream>
#include <string>
//#include <time>
#include "misc.h"
#include "Watch.h"
#include <sys\timeb.h> 
#include "Uci.h"
#include "Position.h"
#include "BrainSwitch.h"
#include "Types.h"
#include "search.h"
using namespace Search;
using namespace std;

  // FEN string of the initial position, normal chess
  const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

Uci::Uci(Engine* eng)
{
	this->engine = eng;
}

Uci::~Uci(void)
{
}

void Uci::Loop(){

  //engine->SetPosition(); // The root position
  Position pos;
	string cmd, token;

  while (token != "quit")
  {
	  Threads.DisplayStatus();
      if (!getline(cin, cmd)) // Block here waiting for input
          cmd = "quit";

      istringstream is(cmd);

      is >> skipws >> token;

      if (token == "quit" || token == "stop")
		  //engine->StopThinking();
		  engine->Stop();

      else if (token == "ponderhit")
      {
          // The opponent has played the expected move. GUI sends "ponderhit" if
          // we were told to ponder on the same move the opponent has played. We
          // should continue searching but switching from pondering to normal search.
		  //Search::Limits.ponder = false;
		  engine->SetPonderLimit(false);
		  //  Search::Signals.stopOnPonderhit)
          if (engine->IsStopOnPonderHit())
			  //engine->StopThinking();
			  engine->Stop();
      }

      else if (token == "go")
          go(pos, is);

      else if (token == "ucinewgame")
          //pos.from_fen(StartFEN, false);
		  engine->SetStartPosition();

	  else if (token == "isready")
          cout << "readyok" << endl;

      else if (token == "position")
		  set_position(pos, is);

      else if (token == "setoption")
          set_option(is);

      else if (token == "perft")
          perft(pos, is);

      else if (token == "d")
          pos.print();

      else if (token == "flip")
          //pos.flip_me();
		  engine->FlipBoard();

      else if (token == "eval")
      {
          //read_evaluation_uci_options(pos.side_to_move());
          engine->ReadEvaluation();
		  
		  //cout << trace_evaluate(pos) << endl;
		  engine->TraceEvaluate();
      }
/*
      else if (token == "key")
          cout << "key: " << hex     << pos.key()
               << "\nmaterial key: " << pos.material_key()
               << "\npawn key: "     << pos.pawn_key() << endl;
*/
      else if (token == "uci")
          cout << engine->GetEngineInfo() << "\n"
               << engine->GetEngineOptions()
               << "\nuciok"      << endl;
	  else if ( token == "test" )
		  test(is);
	  else if ( token == "bench" )
		  bench(is);
      else
          cout << "Unknown command: " << cmd << endl;
  }

}


void Uci::set_position(Position& pos, istringstream& is) {

    Move m;
    string token, fen;

    is >> token;

    if (token == "startpos")
    {
        fen = StartFEN;
        is >> token; // Consume "moves" token if any
    }
    else if (token == "fen")
        while (is >> token && token != "moves")
            fen += token + " ";
    else
        return;

	//pos.from_fen(fen, Options["UCI_Chess960"]);
	engine->SetPosition(fen);

    // Parse move list (if any)
    while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
    {
		//TODO: take current position in consideration for stringToMove method (cPiece and cColor are missing in generated move)
		//m = move_from_uci(pos, token);
		//m = stringToMove(token);

        //pos.do_move(m, *SetupState);
		//engine->DoMove(m, *SetupState);
		engine->currPos.Play(m);
		
		/*
        // Increment pointer to StateRingBuf circular buffer
        if (++SetupState - StateRingBuf >= 102)
            SetupState = StateRingBuf;
			*/
    }
  }
  

  // set_option() is called when engine receives the "setoption" UCI command. The
  // function updates the UCI option ("name") to the given value ("value").

void Uci::set_option(istringstream& is) {

    string token, name, value;

    is >> token; // Consume "name" token

    // Read option name (can contain spaces)
    while (is >> token && token != "value")
        name += string(" ", !name.empty()) + token;

    // Read option value (can contain spaces)
    while (is >> token)
        value += string(" ", !value.empty()) + token;

	//if (!Options.count(name))
    if (!engine->IsValidOption(name))
        cout << "No such option: " << name << endl;
/*
    else if (value.empty()) // UCI buttons don't have a value
        Options[name] = true;

    else
        Options[name] = value;
		*/
	else 
		engine->SetOption(name,value);
  }


  // go() is called when engine receives the "go" UCI command. The function sets
  // the thinking time and other parameters from the input string, and then starts
  // the main searching thread.

void Uci::go(Position& pos, istringstream& is) {

    string token;
    LimitsType limits;
    std::set<Move> searchMoves;
    int time[] = { 0, 0 }, inc[] = { 0, 0 };

    while (is >> token)
    {
		if (token == "infinite") {
            engine->SetLimitsInfinite(true);//limits.infinite = true;
			engine->brainMode = INFINITE_MODE;
		}
		else if (token == "ponder") {
            engine->SetLimitsPonder(true);//limits.ponder = true;
			engine->SetPonderLimit(true);
		}
        else if (token == "wtime"){
            is >> time[WHITE];
			//is >> engine->wtime;
		}
        else if (token == "btime"){
            is >> time[BLACK];
			//is >> engine->btime;
		}
        else if (token == "winc"){
            is >> inc[WHITE];
			//is >> engine->winc;
		}
        else if (token == "binc"){
            is >> inc[BLACK];
			//is >> engine->binc;
		}
        else if (token == "movestogo"){
            is >> limits.movestogo;
		}
        else if (token == "depth"){
			is >> limits.depth;
			engine->brainMode = DEPTH_MODE;
		}
        else if (token == "nodes"){
            //is >> limits.maxNodes;
			is >> engine->maxDepth;
			engine->brainMode = NODES_MODE;
		}
        else if (token == "movetime"){
            //is >> limits.maxTime;
			is >> engine->thinkDelay;
			engine->brainMode = MOVETIME_MODE;
		}
        else if (token == "searchmoves"){
            // TODO: refactor this part
			//while (is >> token)
            //    searchMoves.insert(move_from_uci(pos, token));
		}
    }

	//limits.time = time[/*pos.side_to_move()*/ pos.turn?0:1];
    //limits.increment = inc[/*pos.side_to_move()*/ pos.turn?0:1];

	
    //Threads.start_thinking(pos, limits, searchMoves, true);
	//engine->StartThinking(pos, limits, searchMoves, true);
	engine->Start();
  }


  // perft() is called when engine receives the "perft" command. The function
  // calls perft() with the required search depth then prints counted leaf nodes
  // and elapsed time.

void Uci::perft(Position& pos, istringstream& is) {

    int depth, time;

    if (!(is >> depth))
        return;

	// TODO: use watch (to be improved)
	Watch w;
	w.Start();
    //time = system_time();

    //int64_t n = Search::perft(pos, depth * ONE_PLY); long ???
	long long n = perft(pos, depth * ONE_PLY);

	// TODO: use watch
	w.Stop();
	time = w.GetElapsedTime();
    //time = system_time() - time;

    std::cout << "\nNodes " << n
              << "\nTime (ms) " << time
              << "\nNodes/second " << int(n / (time / 1000.0)) << std::endl;
}

/// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned.

//int64_t
long long Uci::perft(Position& pos, int depth) {

  //StateInfo st;
  //int64_t cnt = 0;
  long long cnt = 0;
  /*

  MoveList<MV_LEGAL> ml(pos);

  // At the last ply just return the number of moves (leaf nodes)
  if (depth == ONE_PLY)
      return ml.size();

  CheckInfo ci(pos);
  for ( ; !ml.end(); ++ml)
  {
      pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci));
      cnt += perft(pos, depth - ONE_PLY);
      pos.undo_move(ml.move());
  }
  */
  return cnt;
}


/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
/*
Move Uci::move_from_uci(const Position& pos, const string& str) {
  for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
      if (str == move_to_uci(ml.move(), pos.is_chess960()))
          return ml.move();
	return MOVE_NONE;


  return NULL;
}*/
/*
int Uci::system_time() {
  struct _timeb t;
  _ftime(&t);
  return int(t.time * 1000 + t.millitm);
}
*/
void Uci::bench(std::istringstream &is){

    string partToBench;

    if (!(is >> partToBench))
        return;
	engine->Bench(partToBench);
}

void Uci::test(std::istringstream &is){

    string partToTest;

    if (!(is >> partToTest))
        return;
	engine->Test(partToTest);
}

